package com.example.filter;


import com.example.config.ReqInfoContext;
import com.example.mdc.MdcUtil;
import com.example.utils.Const;
import com.example.utils.IpUtil;
import com.forum.service.utils.RedisUtil;
import jakarta.annotation.Resource;
import jakarta.servlet.FilterChain;
import jakarta.servlet.ServletException;
import jakarta.servlet.ServletRequest;
import jakarta.servlet.ServletResponse;
import jakarta.servlet.http.HttpFilter;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;
import java.net.URLDecoder;
import java.time.LocalDate;
import java.util.concurrent.TimeUnit;

/**
 * @author lbwxxc
 * @date 2025/4/1 20:11
 * @description: 使 request 可以多次读
 */
@Order(Const.ORDER_REQ)
@Component
@Slf4j
public class ReqRecordFilter extends HttpFilter {
    private static Logger REQ_LOG = LoggerFactory.getLogger("req");
    @Resource
    RedisUtil redisUtil;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest request;
        request = this.initReqInfo((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
        // 生成 traceId 用于追踪请求
        MdcUtil.addTraceId();
        chain.doFilter(request, servletResponse);
        // 记录请求日志
        buildRequestLog(request, ReqInfoContext.getReqInfo());
        MdcUtil.clear();
        ReqInfoContext.clear();
    }

    private HttpServletRequest initReqInfo(HttpServletRequest request, HttpServletResponse response) {
        ReqInfoContext.ReqInfo reqInfo = new ReqInfoContext.ReqInfo();
        // 将 reqInfo 传入 BodyReaderHttpServletRequestWrapper 初始化 reqInfo.Payload
        request = new BodyReaderHttpServletRequestWrapper(request, reqInfo);
        // 初始化 Path， Host， Referer， UserAgent
        reqInfo.setPath(request.getPathInfo());
        reqInfo.setHost(request.getHeader("host"));
        reqInfo.setReferer(request.getHeader("referer"));
        reqInfo.setUserAgent(request.getHeader("User-Agent"));
        reqInfo.setClientIp(IpUtil.getClientIp(request));
        // 将用户信息添加到上下文中
        ReqInfoContext.addReqInfo(reqInfo);

        // 更新 uv/pv 计数
        this.saveVisitInfo(reqInfo.getClientIp(), reqInfo.getPath());

        return request;
    }

    /**
     * @param: request
     * @param: req
     * @return: void
     * @description: 记录请求日志
     */
    private void buildRequestLog(HttpServletRequest request, ReqInfoContext.ReqInfo req) {
        if (req == null) {
            return;
        }
        StringBuilder msg = new StringBuilder();
        msg.append("method=").append(request.getMethod()).append("; ");
        if (StringUtils.isNotBlank(req.getReferer())) {
            msg.append("referer=").append(URLDecoder.decode(req.getReferer())).append("; ");
        }
        msg.append("remoteIp=").append(req.getClientIp());
        msg.append("; agent=").append(req.getUserAgent());

        if (req.getId() != null) {
            // 打印用户信息
            msg.append("; user=").append(req.getId());
        }
        msg.append("; uri=").append(request.getRequestURI());
        if (StringUtils.isNotBlank(request.getQueryString())) {
            msg.append('?').append(URLDecoder.decode(request.getQueryString()));
        }
        msg.append("; payload=").append(req.getPayload());
        REQ_LOG.info(msg.toString());
    }

    /**
     * @param: visitIp
     * @param: path
     * @return: void
     * @description: 更新 pv/uv 奇数
     */
    public void saveVisitInfo(String visitIp, String path) {
        String globalKey = Const.SITE_VISIT_KEY;
        String todayKey = Const.SITE_VISIT_KEY + "_" + Const.day(LocalDate.now());

        int globalUserVisitCnt = redisUtil.hashIncr(globalKey + ":" + visitIp, "pv", 1);
        int todayUserVisitCnt = redisUtil.hashIncr(todayKey, "pv:" + visitIp, 1, 24, TimeUnit.HOURS);


    }
}
